package com.li.mylock;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
//@Component 引入DistributedLockFactory工厂模式，从工厂获得即可,不需要加@Component
public class MyDistributedLock implements Lock {
    private StringRedisTemplate redisTemplate;

    private String lockName;//KEYS[1]
    private String uuidValue;//ARGV[1]
    private long   expireTime;//ARGV[2]
    @Override
    public void lock() {
        tryLock();
    }
//由父类赋值,保证uuid等值不会变化
    public MyDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName, String uuid)
    {
        this.redisTemplate = stringRedisTemplate;
        this.lockName = lockName;
        this.uuidValue = uuid+":"+Thread.currentThread().getId();
        this.expireTime = 30L;
    }

    @Override
    public boolean tryLock() {
        try {
            return tryLock(-1L, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *自动续期
     */
    private void renewExpire()
    {
        String script =
                "if redis.call('HEXISTS',KEYS[1],ARGV[1]) == 1 then     " +
                        "return redis.call('expire',KEYS[1],ARGV[2]) " +
                        "else     " +
                        "return 0 " +
                        "end";

        new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                if (redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime)))
                {
                    renewExpire();
                }
            }
        },(this.expireTime * 1000)/3);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        if (time == -1L){//执行script lua脚本 hset
            String script="if redis.call('exists',KEYS[1])==0 or redis.call('hexists',KEYS[1],ARGV[1])==1" +
                    "then redis.call('hincrby',KEYS[1],ARGV[1],1)" +
                          "redis.call('expire',KEYS[1],ARGV[2])" +
                    "return 1" +
                    "else" +
                    "return 0" +
                    "end";
            while(!redisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class), Arrays.asList(lockName),uuidValue,String.valueOf(expireTime))){
                TimeUnit.MICROSECONDS.sleep(60);
                //新建一个后台扫描程序 ,来坚持key目前的ttl ,是否达到规定的1/2 ,1/3来实现续期
                renewExpire();
                return true;
            }
        }
        return false;
    }


    @Override
    public void unlock() {

    }
    //这两个不需要实现
    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
